home *** CD-ROM | disk | FTP | other *** search
/ InterCD 2000 September / september_2000.iso / intercd / root / ^Linux / WindowMaker / src / workspace.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-12-18  |  27.2 KB  |  1,034 lines

  1. /* workspace.c- Workspace management
  2.  * 
  3.  *  Window Maker window manager
  4.  * 
  5.  *  Copyright (c) 1997, 1998 Alfredo K. Kojima
  6.  * 
  7.  *  This program is free software; you can redistribute it and/or modify
  8.  *  it under the terms of the GNU General Public License as published by
  9.  *  the Free Software Foundation; either version 2 of the License, or
  10.  *  (at your option) any later version.
  11.  *
  12.  *  This program is distributed in the hope that it will be useful,
  13.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15.  *  GNU General Public License for more details.
  16.  *
  17.  *  You should have received a copy of the GNU General Public License
  18.  *  along with this program; if not, write to the Free Software
  19.  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
  20.  *  USA.
  21.  */
  22. #include "wconfig.h"
  23.  
  24. #include <X11/Xlib.h>
  25. #include <X11/Xutil.h>
  26. #ifdef SHAPE
  27. #include <X11/extensions/shape.h>
  28. #endif
  29.  
  30. #include <stdlib.h>
  31. #include <stdio.h>
  32. #include <unistd.h>
  33. #include <ctype.h>
  34. #include <string.h>
  35. #include <time.h>
  36.  
  37. #include "WindowMaker.h"
  38. #include "wcore.h"
  39. #include "framewin.h"
  40. #include "window.h"
  41. #include "icon.h"
  42. #include "funcs.h"
  43. #include "menu.h"
  44. #include "application.h"
  45. #include "dock.h"
  46. #include "actions.h"
  47. #include "workspace.h"
  48. #include "appicon.h"
  49. #ifdef GNOME_STUFF
  50. #include "gnome.h"
  51. #endif
  52. #ifdef KWM_HINTS
  53. #include "kwm.h"
  54. #endif
  55.  
  56. #include <proplist.h>
  57.  
  58.  
  59. extern WPreferences wPreferences;
  60. extern XContext wWinContext;
  61.  
  62.  
  63. static proplist_t dWorkspaces=NULL;
  64. static proplist_t dClip, dName;
  65.  
  66. #ifdef VIRTUAL_DESKTOP
  67. static BOOL initVDesk=False;
  68. #endif
  69.  
  70. static void
  71. make_keys()
  72. {
  73.     if (dWorkspaces!=NULL)
  74.     return;
  75.     
  76.     dWorkspaces = PLMakeString("Workspaces");
  77.     dName = PLMakeString("Name");
  78.     dClip = PLMakeString("Clip");
  79. }
  80.  
  81.  
  82. void
  83. wWorkspaceMake(WScreen *scr, int count)
  84. {
  85.     while (count>0) {
  86.     wWorkspaceNew(scr);
  87.     count--;
  88.     }
  89. }
  90.  
  91.  
  92. int
  93. wWorkspaceNew(WScreen *scr)
  94. {
  95.     WWorkspace *wspace, **list;
  96.     int i;
  97.     
  98.     if (scr->workspace_count < MAX_WORKSPACES) {
  99.     scr->workspace_count++;
  100.  
  101.     wspace = wmalloc(sizeof(WWorkspace));
  102.     wspace->name = NULL;
  103.  
  104. #ifdef KWM_HINTS
  105.     if (scr->flags.kwm_syncing_count) {
  106.         wspace->name = wKWMGetWorkspaceName(scr, scr->workspace_count-1);
  107.     }
  108. #endif
  109.     if (!wspace->name) {
  110.         wspace->name = wmalloc(strlen(_("Workspace %i"))+8);
  111.         sprintf(wspace->name, _("Workspace %i"), scr->workspace_count);
  112.     }
  113.  
  114.  
  115.         if (!wPreferences.flags.noclip) {
  116.             wspace->clip = wDockCreate(scr, WM_CLIP);
  117.     } else
  118.             wspace->clip = NULL;
  119.  
  120.     list = wmalloc(sizeof(WWorkspace*)*scr->workspace_count);
  121.  
  122.     for (i=0; i<scr->workspace_count-1; i++) {
  123.         list[i] = scr->workspaces[i];
  124.     }
  125.     list[i] = wspace;
  126.     if (scr->workspaces)
  127.         free(scr->workspaces);
  128.     scr->workspaces = list;
  129.  
  130.     wWorkspaceMenuUpdate(scr, scr->workspace_menu);
  131.     wWorkspaceMenuUpdate(scr, scr->clip_ws_menu);
  132. #ifdef GNOME_STUFF
  133.     wGNOMEUpdateWorkspaceHints(scr);
  134. #endif
  135. #ifdef KWM_HINTS
  136.     if (!scr->flags.kwm_syncing_count) {
  137.         wKWMUpdateWorkspaceCountHint(scr);
  138.         wKWMUpdateWorkspaceNameHint(scr, scr->workspace_count-1);
  139.     }
  140. #ifdef not_used
  141.     wKWMSetUsableAreaHint(scr, scr->workspace_count-1);
  142. #endif
  143. #endif
  144.     XFlush(dpy);
  145.  
  146.     return scr->workspace_count-1;
  147.     }
  148.     return -1;
  149. }
  150.  
  151.  
  152.  
  153. Bool
  154. wWorkspaceDelete(WScreen *scr, int workspace)
  155. {
  156.     WWindow *tmp;
  157.     WWorkspace **list;
  158.     int i, j;
  159.  
  160.  
  161.     if (workspace<=0)
  162.     return False;
  163.  
  164.     /* verify if workspace is in use by some window */
  165.     tmp = scr->focused_window;
  166.     while (tmp) {
  167.     if (!IS_OMNIPRESENT(tmp) && tmp->frame->workspace==workspace)
  168.         return False;
  169.     tmp = tmp->prev;
  170.     }
  171.  
  172.     if (!wPreferences.flags.noclip) {
  173.         wDockDestroy(scr->workspaces[workspace]->clip);
  174.         scr->workspaces[workspace]->clip = NULL;
  175.     }
  176.     
  177.     list = wmalloc(sizeof(WWorkspace*)*(scr->workspace_count-1));
  178.     j = 0;
  179.     for (i=0; i<scr->workspace_count; i++) {
  180.     if (i!=workspace)
  181.         list[j++] = scr->workspaces[i];
  182.     else {
  183.         if (scr->workspaces[i]->name)
  184.         free(scr->workspaces[i]->name);
  185.         free(scr->workspaces[i]);
  186.     }
  187.     }
  188.     free(scr->workspaces);
  189.     scr->workspaces = list;
  190.  
  191.     scr->workspace_count--;
  192.  
  193.  
  194.     /* update menu */
  195.     wWorkspaceMenuUpdate(scr, scr->workspace_menu);
  196.     /* clip workspace menu */
  197.     wWorkspaceMenuUpdate(scr, scr->clip_ws_menu);
  198.  
  199.     /* update also window menu */
  200.     if (scr->workspace_submenu) {
  201.     WMenu *menu = scr->workspace_submenu;
  202.  
  203.         i = menu->entry_no;
  204.         while (i>scr->workspace_count)
  205.             wMenuRemoveItem(menu, --i);
  206.     wMenuRealize(menu);
  207.     }
  208.     /* and clip menu */
  209.     if (scr->clip_submenu) {
  210.         WMenu *menu = scr->clip_submenu;
  211.  
  212.         i = menu->entry_no;
  213.         while (i>scr->workspace_count)
  214.             wMenuRemoveItem(menu, --i);
  215.     wMenuRealize(menu);
  216.     }
  217.  
  218. #ifdef GNOME_STUFF
  219.     wGNOMEUpdateWorkspaceHints(scr);
  220. #endif
  221. #ifdef KWM_HINTS
  222.     wKWMUpdateWorkspaceCountHint(scr);
  223. #endif
  224.  
  225.     if (scr->current_workspace >= scr->workspace_count)
  226.     wWorkspaceChange(scr, scr->workspace_count-1);
  227.  
  228.     return True;
  229. }
  230.  
  231.  
  232. typedef struct WorkspaceNameData {
  233.     int count;
  234.     RImage *back;
  235.     RImage *text;
  236.     time_t timeout;
  237. } WorkspaceNameData;
  238.  
  239.  
  240.  
  241. static void
  242. hideWorkpaceName(void *data)
  243. {
  244.     WScreen *scr = (WScreen*)data;
  245.  
  246.     if (!scr->workspace_name_data || scr->workspace_name_data->count == 0
  247.     || time(NULL) > scr->workspace_name_data->timeout) {
  248.     XUnmapWindow(dpy, scr->workspace_name);
  249.  
  250.     if (scr->workspace_name_data) {
  251.         RDestroyImage(scr->workspace_name_data->back);
  252.         RDestroyImage(scr->workspace_name_data->text);
  253.         free(scr->workspace_name_data);
  254.  
  255.         scr->workspace_name_data = NULL;
  256.     }
  257.     scr->workspace_name_timer = NULL;
  258.     } else {
  259.     RImage *img = RCloneImage(scr->workspace_name_data->back);
  260.     Pixmap pix;
  261.  
  262.     scr->workspace_name_timer = 
  263.         WMAddTimerHandler(WORKSPACE_NAME_FADE_DELAY, hideWorkpaceName,
  264.                   scr);
  265.  
  266.     RCombineImagesWithOpaqueness(img, scr->workspace_name_data->text,
  267.                      scr->workspace_name_data->count*255/10);
  268.  
  269.     RConvertImage(scr->rcontext, img, &pix);
  270.  
  271.     RDestroyImage(img);
  272.  
  273.     XSetWindowBackgroundPixmap(dpy, scr->workspace_name, pix);
  274.     XClearWindow(dpy, scr->workspace_name);
  275.     XFreePixmap(dpy, pix);
  276.     XFlush(dpy);
  277.  
  278.     scr->workspace_name_data->count--;
  279.     }
  280. }
  281.  
  282.  
  283.  
  284. static void
  285. showWorkspaceName(WScreen *scr, int workspace)
  286. {
  287.     WorkspaceNameData *data;
  288.     RXImage *ximg;
  289.     Pixmap text, mask;
  290.     int w, h;
  291.     int px, py;
  292.     char *name = scr->workspaces[workspace]->name;
  293.     int len = strlen(name);
  294.     int x, y;
  295.  
  296.     if (wPreferences.workspace_name_display_position == WD_NONE
  297.     || scr->workspace_count < 2)
  298.     return;
  299.  
  300.     if (scr->workspace_name_timer) {
  301.     WMDeleteTimerHandler(scr->workspace_name_timer);
  302.     XUnmapWindow(dpy, scr->workspace_name);
  303.     XFlush(dpy);
  304.     }
  305.     scr->workspace_name_timer = WMAddTimerHandler(WORKSPACE_NAME_DELAY,
  306.                           hideWorkpaceName, scr);
  307.  
  308.     if (scr->workspace_name_data) {
  309.     RDestroyImage(scr->workspace_name_data->back);
  310.     RDestroyImage(scr->workspace_name_data->text);
  311.     free(scr->workspace_name_data);
  312.     }
  313.  
  314.     data = wmalloc(sizeof(WorkspaceNameData));
  315.  
  316.     w = WMWidthOfString(scr->workspace_name_font, name, len);
  317.     h = WMFontHeight(scr->workspace_name_font);
  318.  
  319.     switch (wPreferences.workspace_name_display_position) {
  320.      case WD_TOP:
  321.     px = (scr->scr_width - (w+4))/2;
  322.     py = 0;
  323.     break;
  324.      case WD_BOTTOM:
  325.     px = (scr->scr_width - (w+4))/2;
  326.     py = scr->scr_height - (h+4);
  327.     break;
  328.      case WD_TOPLEFT:
  329.     px = 0;
  330.     py = 0;
  331.     break;
  332.      case WD_TOPRIGHT:
  333.     px = scr->scr_width - (w+4);
  334.     py = 0;
  335.     break;
  336.      case WD_BOTTOMLEFT:
  337.     px = 0;
  338.     py = scr->scr_height - (h+4);
  339.     break;
  340.      case WD_BOTTOMRIGHT:
  341.     px = scr->scr_width - (w+4);
  342.     py = scr->scr_height - (h+4);
  343.     break;
  344.      case WD_CENTER:
  345.      default:
  346.     px = (scr->scr_width - (w+4))/2;
  347.     py = (scr->scr_height - (h+4))/2;
  348.     break;
  349.     }
  350.     XResizeWindow(dpy, scr->workspace_name, w+4, h+4);
  351.     XMoveWindow(dpy, scr->workspace_name, px, py);
  352.  
  353.     text = XCreatePixmap(dpy, scr->w_win, w+4, h+4, scr->w_depth);
  354.     mask = XCreatePixmap(dpy, scr->w_win, w+4, h+4, 1);
  355.  
  356.     XSetForeground(dpy, scr->draw_gc, scr->black_pixel);
  357.     XFillRectangle(dpy, text, scr->draw_gc, 0, 0, w+4, h+4);
  358.  
  359.     XSetForeground(dpy, scr->mono_gc, 0);
  360.     XFillRectangle(dpy, mask, scr->mono_gc, 0, 0, w+4, h+4);
  361.  
  362.     XSetForeground(dpy, scr->mono_gc, 1);
  363.     for (x = 0; x <= 4; x++) {
  364.     for (y = 0; y <= 4; y++) {
  365.         WMDrawString(scr->wmscreen, mask, scr->mono_gc,
  366.              scr->workspace_name_font, x, y, name, len);
  367.     }
  368.     }
  369.  
  370.     XSetForeground(dpy, scr->draw_gc, scr->white_pixel);
  371.     WMDrawString(scr->wmscreen, text, scr->draw_gc, scr->workspace_name_font, 
  372.          2, 2, scr->workspaces[workspace]->name,
  373.          strlen(scr->workspaces[workspace]->name));
  374. #ifdef SHAPE
  375.     XShapeCombineMask(dpy, scr->workspace_name, ShapeBounding, 0, 0, mask,
  376.               ShapeSet);
  377. #endif
  378.     XSetWindowBackgroundPixmap(dpy, scr->workspace_name, text);
  379.     XClearWindow(dpy, scr->workspace_name);
  380.  
  381.     data->text = RCreateImageFromDrawable(scr->rcontext, text, None);
  382.  
  383.     XFreePixmap(dpy, text);
  384.     XFreePixmap(dpy, mask);
  385.  
  386.     if (!data->text) {
  387.     XMapRaised(dpy, scr->workspace_name);
  388.     XFlush(dpy);
  389.  
  390.     goto erro;
  391.     }
  392.  
  393.     ximg = RGetXImage(scr->rcontext, scr->root_win, px, py,
  394.               data->text->width, data->text->height);
  395.  
  396.     if (!ximg || !ximg->image) {
  397.     goto erro;
  398.     }
  399.  
  400.     XMapRaised(dpy, scr->workspace_name);
  401.     XFlush(dpy);
  402.  
  403.     data->back = RCreateImageFromXImage(scr->rcontext, ximg->image, NULL);
  404.     RDestroyXImage(scr->rcontext, ximg);
  405.  
  406.     if (!data->back) {
  407.     goto erro;
  408.     }
  409.  
  410.     data->count = 10;
  411.  
  412.     /* set a timeout for the effect */
  413.     data->timeout = time(NULL) + 2 +
  414.     (WORKSPACE_NAME_DELAY + WORKSPACE_NAME_FADE_DELAY*data->count)/1000;
  415.  
  416.     scr->workspace_name_data = data;
  417.  
  418.  
  419.     return;
  420.  
  421. erro:
  422.     if (scr->workspace_name_timer)
  423.     WMDeleteTimerHandler(scr->workspace_name_timer);
  424.  
  425.     if (data->text)
  426.     RDestroyImage(data->text);
  427.     if (data->back)
  428.     RDestroyImage(data->back);
  429.     free(data);
  430.  
  431.     scr->workspace_name_data = NULL;
  432.  
  433.     scr->workspace_name_timer = WMAddTimerHandler(WORKSPACE_NAME_DELAY +
  434.                           10*WORKSPACE_NAME_FADE_DELAY,
  435.                           hideWorkpaceName, scr);
  436. }
  437.  
  438.  
  439. void
  440. wWorkspaceChange(WScreen *scr, int workspace)
  441. {
  442.     if (scr->flags.startup || scr->flags.startup2) {
  443.     return;
  444.     }
  445.  
  446.     if (workspace != scr->current_workspace) {
  447.         wWorkspaceForceChange(scr, workspace);
  448.     } /*else {
  449.     showWorkspaceName(scr, workspace);
  450.     }*/
  451. }
  452.  
  453.  
  454. void
  455. wWorkspaceRelativeChange(WScreen *scr, int amount)
  456. {
  457.     int w;
  458.  
  459.     w = scr->current_workspace + amount;
  460.  
  461.     if (amount < 0) {
  462.  
  463.     if (w >= 0)
  464.         wWorkspaceChange(scr, w);
  465.     else if (wPreferences.ws_cycle)
  466.         wWorkspaceChange(scr, scr->workspace_count + w);
  467.  
  468.     } else if (amount > 0) {
  469.  
  470.     if (w < scr->workspace_count)
  471.         wWorkspaceChange(scr, w);
  472.     else if (wPreferences.ws_advance)
  473.         wWorkspaceChange(scr, WMIN(w, MAX_WORKSPACES-1));
  474.     else if (wPreferences.ws_cycle)
  475.         wWorkspaceChange(scr, w % scr->workspace_count);
  476.     }
  477. }
  478.  
  479.  
  480.  
  481. void
  482. wWorkspaceForceChange(WScreen *scr, int workspace)
  483. {
  484.     WWindow *tmp, *foc=NULL, *foc2=NULL;
  485.     
  486.     if (workspace >= MAX_WORKSPACES || workspace < 0)
  487.     return;
  488.  
  489.     SendHelperMessage(scr, 'C', workspace+1, NULL);
  490.  
  491.     if (workspace > scr->workspace_count-1) {
  492.     wWorkspaceMake(scr, workspace - scr->workspace_count + 1);
  493.     }
  494.  
  495.     wClipUpdateForWorkspaceChange(scr, workspace);
  496.  
  497.     scr->current_workspace = workspace;
  498.  
  499.     wWorkspaceMenuUpdate(scr, scr->workspace_menu);
  500.  
  501.     wWorkspaceMenuUpdate(scr, scr->clip_ws_menu);
  502.  
  503.     if ((tmp = scr->focused_window)!= NULL) {
  504.     if ((IS_OMNIPRESENT(tmp) && !WFLAGP(tmp, skip_window_list))
  505.         || tmp->flags.changing_workspace) {
  506.         foc = tmp;
  507.     }
  508.  
  509.     /* foc2 = tmp; will fix annoyance with gnome panel
  510.      * but will create annoyance for every other application 
  511.      */
  512.  
  513.     while (tmp) {
  514.         if (tmp->frame->workspace!=workspace && !tmp->flags.selected) {
  515.         /* unmap windows not on this workspace */
  516.         if ((tmp->flags.mapped||tmp->flags.shaded)
  517.             && !IS_OMNIPRESENT(tmp)
  518.             && !tmp->flags.changing_workspace) {
  519.             
  520.             wWindowUnmap(tmp);
  521.         }
  522.                 /* also unmap miniwindows not on this workspace */
  523.                 if (tmp->flags.miniaturized && !IS_OMNIPRESENT(tmp) 
  524.             && tmp->icon) {
  525.                     if (!wPreferences.sticky_icons) {
  526.                         XUnmapWindow(dpy, tmp->icon->core->window);
  527.             tmp->icon->mapped = 0;
  528.             }
  529. #if 0
  530.             else {
  531.             tmp->icon->mapped = 1;
  532.             /* Why is this here? -Alfredo */
  533.                         XMapWindow(dpy, tmp->icon->core->window);
  534.             }
  535. #endif
  536.                 }
  537.         /* update current workspace of omnipresent windows */
  538.         if (IS_OMNIPRESENT(tmp)) {
  539.             WApplication *wapp = wApplicationOf(tmp->main_window);
  540.  
  541.             tmp->frame->workspace = workspace;
  542.  
  543.             if (wapp) {
  544.             wapp->last_workspace = workspace;
  545.             }
  546.             if (!foc2)
  547.             foc2 = tmp;
  548.         }
  549.             } else {
  550.         /* change selected windows' workspace */
  551.         if (tmp->flags.selected) {
  552.             wWindowChangeWorkspace(tmp, workspace);
  553.                     if (!tmp->flags.miniaturized && !foc) {
  554.                         foc = tmp;
  555.                     }
  556.                 } else {
  557.             if (!tmp->flags.hidden) {
  558.             if (!(tmp->flags.mapped || tmp->flags.miniaturized)) {
  559.                 /* remap windows that are on this workspace */
  560.                 wWindowMap(tmp);
  561.                 if (!foc && !WFLAGP(tmp, skip_window_list))
  562.                 foc = tmp;
  563.             }
  564.             /* Also map miniwindow if not omnipresent */
  565.             if (!wPreferences.sticky_icons &&
  566.                 tmp->flags.miniaturized &&
  567.                 !IS_OMNIPRESENT(tmp) && tmp->icon) {
  568.                 tmp->icon->mapped = 1;
  569.                 XMapWindow(dpy, tmp->icon->core->window);
  570.             }
  571.             }
  572.                 }
  573.         }
  574.         tmp = tmp->prev;
  575.     }
  576.  
  577.     if (!foc)
  578.         foc = foc2;
  579.  
  580.     if (scr->focused_window->flags.mapped && !foc) {
  581.         foc = scr->focused_window;
  582.     }
  583.     if (wPreferences.focus_mode == WKF_CLICK) {
  584.         wSetFocusTo(scr, foc);
  585.     } else {
  586.             unsigned int mask;
  587.             int foo;
  588.             Window bar, win;
  589.         WWindow *tmp;
  590.  
  591.         tmp = NULL;
  592.             if (XQueryPointer(dpy, scr->root_win, &bar, &win,
  593.                               &foo, &foo, &foo, &foo, &mask)) {
  594.         tmp = wWindowFor(win);
  595.         }
  596.         if (!tmp && wPreferences.focus_mode == WKF_SLOPPY) {
  597.         wSetFocusTo(scr, foc);
  598.         } else {
  599.         wSetFocusTo(scr, tmp);
  600.         }
  601.     }
  602.     }
  603.  
  604.     /* We need to always arrange icons when changing workspace, even if
  605.      * no autoarrange icons, because else the icons in different workspaces
  606.      * can be superposed.
  607.      * This can be avoided if appicons are also workspace specific.
  608.      */
  609.     if (!wPreferences.sticky_icons)
  610.         wArrangeIcons(scr, False);
  611.  
  612.     if (scr->dock)
  613.         wAppIconPaint(scr->dock->icon_array[0]);
  614.  
  615.     if (scr->clip_icon) {
  616.         if (scr->workspaces[workspace]->clip->auto_collapse ||
  617.             scr->workspaces[workspace]->clip->auto_raise_lower) {
  618.             /* to handle enter notify. This will also */
  619.             XUnmapWindow(dpy, scr->clip_icon->icon->core->window);
  620.             XMapWindow(dpy, scr->clip_icon->icon->core->window);
  621.         } else {
  622.             wClipIconPaint(scr->clip_icon);
  623.         }
  624.     }
  625.  
  626.     showWorkspaceName(scr, workspace);
  627.  
  628. #ifdef GNOME_STUFF
  629.     wGNOMEUpdateCurrentWorkspaceHint(scr);
  630. #endif
  631. #ifdef KWM_HINTS
  632.     wKWMUpdateCurrentWorkspaceHint(scr);
  633. #endif
  634. /*   XSync(dpy, False); */
  635. }
  636.  
  637. #ifdef VIRTUAL_DESKTOP
  638.  
  639. void wWorkspaceManageEdge(WScreen *scr) {
  640.     int i;
  641.     int vmask;
  642.     XSetWindowAttributes attribs;
  643.  
  644.     puts("ok");
  645.     if (wPreferences.vedge_thickness) {
  646.         initVDesk=True;
  647.         for (i = 0;i < scr->workspace_count; i++) {
  648.             puts("reset workspace");
  649.             scr->workspaces[i]->x = scr->workspaces[i]->y = 0;
  650.             wWorkspaceResizeViewPort(scr, i, wPreferences.vedge_width, wPreferences.vedge_height);
  651.         }
  652.  
  653.         vmask = CWEventMask|CWOverrideRedirect;
  654.         attribs.event_mask = (EnterWindowMask | LeaveWindowMask | VisibilityChangeMask);
  655.         attribs.override_redirect = True;
  656.         scr->virtual_edge_u =
  657.             XCreateWindow(dpy, scr->root_win, 0, 0,
  658.                     scr->scr_width, wPreferences.vedge_thickness, 0,
  659.                     CopyFromParent, InputOnly, CopyFromParent, vmask, &attribs);
  660.         scr->virtual_edge_d =
  661.             XCreateWindow(dpy, scr->root_win, 0, scr->scr_height-wPreferences.vedge_thickness,
  662.                     scr->scr_width, wPreferences.vedge_thickness, 0,
  663.                     CopyFromParent, InputOnly, CopyFromParent, vmask, &attribs);
  664.         scr->virtual_edge_l =
  665.             XCreateWindow(dpy, scr->root_win, 0, 0,
  666.                     wPreferences.vedge_thickness, scr->scr_height, 0,
  667.                     CopyFromParent, InputOnly, CopyFromParent, vmask, &attribs);
  668.         scr->virtual_edge_r =
  669.             XCreateWindow(dpy, scr->root_win, scr->scr_width-wPreferences.vedge_thickness, 0,
  670.                     wPreferences.vedge_thickness, scr->scr_height, 0,
  671.                     CopyFromParent, InputOnly, CopyFromParent, vmask, &attribs);
  672.         XMapWindow(dpy, scr->virtual_edge_u);
  673.         XMapWindow(dpy, scr->virtual_edge_d);
  674.         XMapWindow(dpy, scr->virtual_edge_l);
  675.         XMapWindow(dpy, scr->virtual_edge_r);
  676.         wWorkspaceRaiseEdge(scr);
  677.     }
  678. }
  679.  
  680. void wWorkspaceRaiseEdge(WScreen *scr) {
  681.     if (wPreferences.vedge_thickness && initVDesk) {
  682.         XRaiseWindow(dpy, scr->virtual_edge_u);
  683.         XRaiseWindow(dpy, scr->virtual_edge_d);
  684.         XRaiseWindow(dpy, scr->virtual_edge_l);
  685.         XRaiseWindow(dpy, scr->virtual_edge_r);
  686.     }
  687. }
  688.  
  689. void wWorkspaceResizeViewPort(WScreen *scr, int workspace, int width, int height)
  690. {
  691.     if (width < scr->scr_width) return;
  692.     if (height < scr->scr_height) return;
  693.  
  694.     scr->workspaces[workspace]->width = WMAX(width,scr->scr_width);
  695.     scr->workspaces[workspace]->height = WMAX(height,scr->scr_height);
  696.  
  697. }
  698.  
  699. void wWorkspaceSetViewPort(WScreen *scr, int workspace, int x, int y)
  700. {
  701.     int diff_x, diff_y;
  702.     WWindow *wwin;
  703.  
  704.     if (x < 0) return;
  705.     if (y < 0) return;
  706.     if (x + scr->scr_width > scr->workspaces[workspace]->width) return;
  707.     if (y + scr->scr_height > scr->workspaces[workspace]->height) return;
  708.  
  709.     diff_x = scr->workspaces[workspace]->x - x;
  710.     diff_y = scr->workspaces[workspace]->y - y;
  711.  
  712.     scr->workspaces[workspace]->x = WMIN(x, scr->workspaces[workspace]->width - scr->scr_width);
  713.     scr->workspaces[workspace]->y = WMIN(y, scr->workspaces[workspace]->height - scr->scr_height);
  714.  
  715.     printf("set view %d %d, %d\n",workspace, scr->workspaces[workspace]->x, scr->workspaces[workspace]->y);
  716.  
  717.     wwin = scr->focused_window;
  718.     while (wwin) {
  719.         if (wwin->frame->workspace == workspace) {
  720.             wWindowMove(wwin, wwin->frame_x + diff_x, wwin->frame_y + diff_y);
  721.             wWindowSynthConfigureNotify(wwin);
  722.         }
  723.         wwin = wwin->prev;
  724.     }
  725. }
  726.  
  727. void wWorkspaceGetViewPosition(WScreen *scr, int workspace, int *x, int *y) {
  728.     *x = scr->workspaces[workspace]->x;
  729.     *y = scr->workspaces[workspace]->y;
  730. }
  731.  
  732. #endif
  733.  
  734. static void
  735. switchWSCommand(WMenu *menu, WMenuEntry *entry)
  736. {
  737.     wWorkspaceChange(menu->frame->screen_ptr, (long)entry->clientdata);
  738. }
  739.  
  740.  
  741.  
  742. static void
  743. deleteWSCommand(WMenu *menu, WMenuEntry *entry)
  744. {
  745.     wWorkspaceDelete(menu->frame->screen_ptr, 
  746.              menu->frame->screen_ptr->workspace_count-1);
  747. }
  748.  
  749.  
  750.  
  751. static void
  752. newWSCommand(WMenu *menu, WMenuEntry *foo)
  753. {
  754.     int ws;
  755.  
  756.     ws = wWorkspaceNew(menu->frame->screen_ptr);
  757.     /* autochange workspace*/
  758.     if (ws>=0)
  759.     wWorkspaceChange(menu->frame->screen_ptr, ws);
  760.     
  761.     
  762.     /*
  763.     if (ws<9) {
  764.     int kcode;
  765.     if (wKeyBindings[WKBD_WORKSPACE1+ws]) {
  766.         kcode = wKeyBindings[WKBD_WORKSPACE1+ws]->keycode;
  767.         entry->rtext = 
  768.           wstrdup(XKeysymToString(XKeycodeToKeysym(dpy, kcode, 0)));
  769.     }
  770.     }*/
  771. }
  772.  
  773.  
  774. static char*
  775. cropline(char *line)
  776. {
  777.     char *start, *end;
  778.     
  779.     if (strlen(line)==0)
  780.     return line;
  781.     
  782.     start = line;
  783.     end = &(line[strlen(line)])-1;
  784.     while (isspace(*line) && *line!=0) line++;
  785.     while (isspace(*end) && end!=line) {
  786.     *end=0;
  787.     end--;
  788.     }
  789.     return line;
  790. }
  791.  
  792.  
  793. void
  794. wWorkspaceRename(WScreen *scr, int workspace, char *name)
  795. {
  796.     char buf[MAX_WORKSPACENAME_WIDTH+1];
  797.     char *tmp;
  798.  
  799.     if (workspace >= scr->workspace_count)
  800.     return;
  801.  
  802.     /* trim white spaces */
  803.     tmp = cropline(name);
  804.  
  805.     if (strlen(tmp)==0) {
  806.     sprintf(buf, _("Workspace %i"), workspace+1);
  807.     } else {
  808.     strncpy(buf, tmp, MAX_WORKSPACENAME_WIDTH);
  809.     }
  810.     buf[MAX_WORKSPACENAME_WIDTH] = 0;
  811.  
  812.     /* update workspace */
  813.     free(scr->workspaces[workspace]->name);
  814.     scr->workspaces[workspace]->name = wstrdup(buf);
  815.  
  816.     if (scr->clip_ws_menu) {
  817.     if (strcmp(scr->clip_ws_menu->entries[workspace+2]->text, buf)!=0) {
  818.         free(scr->clip_ws_menu->entries[workspace+2]->text);
  819.         scr->clip_ws_menu->entries[workspace+2]->text = wstrdup(buf);
  820.         wMenuRealize(scr->clip_ws_menu);
  821.     }
  822.     }
  823.     if (scr->workspace_menu) {
  824.     if (strcmp(scr->workspace_menu->entries[workspace+2]->text, buf)!=0) {
  825.         free(scr->workspace_menu->entries[workspace+2]->text);
  826.         scr->workspace_menu->entries[workspace+2]->text = wstrdup(buf);
  827.         wMenuRealize(scr->workspace_menu);
  828.     }
  829.     }
  830.  
  831.     UpdateSwitchMenuWorkspace(scr, workspace);
  832.  
  833.     if (scr->clip_icon)
  834.         wClipIconPaint(scr->clip_icon);
  835.     
  836. #ifdef GNOME_STUFF
  837.     wGNOMEUpdateWorkspaceNamesHint(scr);
  838. #endif
  839. #ifdef KWM_HINTS
  840.     wKWMUpdateWorkspaceNameHint(scr, workspace);
  841. #endif
  842. }
  843.  
  844.  
  845.  
  846.  
  847. /* callback for when menu entry is edited */
  848. static void
  849. onMenuEntryEdited(WMenu *menu, WMenuEntry *entry)
  850. {
  851.     char *tmp;
  852.  
  853.     tmp = entry->text;
  854.     wWorkspaceRename(menu->frame->screen_ptr, (long)entry->clientdata, tmp);
  855. }
  856.  
  857.  
  858. WMenu*
  859. wWorkspaceMenuMake(WScreen *scr, Bool titled)
  860. {
  861.     WMenu *wsmenu;
  862.  
  863.     wsmenu = wMenuCreate(scr, titled ? _("Workspaces") : NULL, False);
  864.     if (!wsmenu) {
  865.     wwarning(_("could not create Workspace menu"));
  866.     return NULL;
  867.     }
  868.     
  869.     /* callback to be called when an entry is edited */
  870.     wsmenu->on_edit = onMenuEntryEdited;
  871.     
  872.     wMenuAddCallback(wsmenu, _("New"), newWSCommand, NULL);
  873.     wMenuAddCallback(wsmenu, _("Destroy Last"), deleteWSCommand, NULL);
  874.  
  875.     return wsmenu;
  876. }
  877.  
  878.  
  879.  
  880. void
  881. wWorkspaceMenuUpdate(WScreen *scr, WMenu *menu)
  882. {
  883.     int i;
  884.     long ws;
  885.     char title[MAX_WORKSPACENAME_WIDTH+1];
  886.     WMenuEntry *entry;
  887.     int tmp;
  888.  
  889.     if (!menu)
  890.     return;
  891.  
  892.     if (menu->entry_no < scr->workspace_count+2) {
  893.     /* new workspace(s) added */
  894.     i = scr->workspace_count-(menu->entry_no-2);
  895.     ws = menu->entry_no - 2;
  896.     while (i>0) {
  897.         strcpy(title, scr->workspaces[ws]->name);
  898.  
  899.         entry = wMenuAddCallback(menu, title, switchWSCommand, (void*)ws);
  900.         entry->flags.indicator = 1;
  901.         entry->flags.editable = 1;
  902.  
  903.         i--;
  904.         ws++;
  905.     }
  906.     } else if (menu->entry_no > scr->workspace_count+2) {
  907.     /* removed workspace(s) */
  908.     for (i = menu->entry_no-1; i >= scr->workspace_count+2; i--) {
  909.         wMenuRemoveItem(menu, i);
  910.     }
  911.     }
  912.     wMenuRealize(menu);
  913.     
  914.     for (i=0; i<scr->workspace_count; i++) {    
  915.     menu->entries[i+2]->flags.indicator_on = 0;
  916.     }
  917.     menu->entries[scr->current_workspace+2]->flags.indicator_on = 1;
  918.  
  919.     /* don't let user destroy current workspace */
  920.     if (scr->current_workspace == scr->workspace_count-1) {
  921.     wMenuSetEnabled(menu, 1, False);
  922.     } else {
  923.     wMenuSetEnabled(menu, 1, True);
  924.     }
  925.  
  926.     tmp = menu->frame->top_width + 5;
  927.     /* if menu got unreachable, bring it to a visible place */
  928.     if (menu->frame_x < tmp - (int)menu->frame->core->width)
  929.     wMenuMove(menu, tmp - (int)menu->frame->core->width, menu->frame_y, False);
  930.     
  931.     wMenuPaint(menu);
  932. }
  933.  
  934.  
  935. void
  936. wWorkspaceSaveState(WScreen *scr, proplist_t old_state)
  937. {
  938.     proplist_t parr, pstr;
  939.     proplist_t wks_state, old_wks_state;
  940.     proplist_t foo, bar;
  941.     int i;
  942.  
  943.     make_keys();
  944.  
  945.     old_wks_state = PLGetDictionaryEntry(old_state, dWorkspaces);
  946.     parr = PLMakeArrayFromElements(NULL);
  947.     for (i=0; i < scr->workspace_count; i++) {
  948.         pstr = PLMakeString(scr->workspaces[i]->name);
  949.         wks_state = PLMakeDictionaryFromEntries(dName, pstr, NULL);
  950.         PLRelease(pstr);
  951.         if (!wPreferences.flags.noclip) {
  952.             pstr = wClipSaveWorkspaceState(scr, i);
  953.             PLInsertDictionaryEntry(wks_state, dClip, pstr);
  954.             PLRelease(pstr);
  955.         } else if (old_wks_state!=NULL) {
  956.             if ((foo = PLGetArrayElement(old_wks_state, i))!=NULL) {
  957.                 if ((bar = PLGetDictionaryEntry(foo, dClip))!=NULL) {
  958.                     PLInsertDictionaryEntry(wks_state, dClip, bar);
  959.                 }
  960.             }
  961.         }
  962.         PLAppendArrayElement(parr, wks_state);
  963.         PLRelease(wks_state);
  964.     }
  965.     PLInsertDictionaryEntry(scr->session_state, dWorkspaces, parr);
  966.     PLRelease(parr);
  967. }
  968.  
  969.  
  970. void
  971. wWorkspaceRestoreState(WScreen *scr)
  972. {
  973.     proplist_t parr, pstr, wks_state;
  974.     proplist_t clip_state;
  975.     int i, j, wscount;
  976.  
  977.     make_keys();
  978.  
  979.     parr = PLGetDictionaryEntry(scr->session_state, dWorkspaces);
  980.  
  981.     if (!parr)
  982.     return;
  983.     
  984.     wscount = scr->workspace_count;
  985.     for (i=0; i < WMIN(PLGetNumberOfElements(parr), MAX_WORKSPACES); i++) {
  986.         wks_state = PLGetArrayElement(parr, i);
  987.         if (PLIsDictionary(wks_state))
  988.             pstr = PLGetDictionaryEntry(wks_state, dName);
  989.         else
  990.             pstr = wks_state;
  991.     if (i >= scr->workspace_count)
  992.         wWorkspaceNew(scr);
  993.     if (scr->workspace_menu) {
  994.         free(scr->workspace_menu->entries[i+2]->text);
  995.         scr->workspace_menu->entries[i+2]->text = wstrdup(PLGetString(pstr));
  996.         scr->workspace_menu->flags.realized = 0;
  997.     }
  998.     free(scr->workspaces[i]->name);
  999.         scr->workspaces[i]->name = wstrdup(PLGetString(pstr));
  1000.         if (!wPreferences.flags.noclip) {
  1001.             clip_state = PLGetDictionaryEntry(wks_state, dClip);
  1002.             if (scr->workspaces[i]->clip)
  1003.                 wDockDestroy(scr->workspaces[i]->clip);
  1004.             scr->workspaces[i]->clip = wDockRestoreState(scr, clip_state,
  1005.                                                          WM_CLIP);
  1006.             if (i>0)
  1007.                 wDockHideIcons(scr->workspaces[i]->clip);
  1008.  
  1009.             /* We set the global icons here, because scr->workspaces[i]->clip
  1010.              * was not valid in wDockRestoreState().
  1011.              * There we only set icon->omnipresent to know which icons we
  1012.              * need to set here.
  1013.              */
  1014.             for (j=0; j<scr->workspaces[i]->clip->max_icons; j++) {
  1015.                 WAppIcon *aicon = scr->workspaces[i]->clip->icon_array[j];
  1016.  
  1017.                 if (aicon && aicon->omnipresent) {
  1018.                     aicon->omnipresent = 0;
  1019.                     wClipMakeIconOmnipresent(aicon, True);
  1020.                     XMapWindow(dpy, aicon->icon->core->window);
  1021.                 }
  1022.             }
  1023.         }
  1024. #ifdef KWM_HINTS
  1025.     wKWMUpdateWorkspaceNameHint(scr, i);
  1026. #endif
  1027.     }
  1028. #ifdef GNOME_STUFF
  1029.     wGNOMEUpdateWorkspaceNamesHint(scr);
  1030. #endif
  1031. }
  1032.  
  1033.  
  1034.